#include "webserver.h"

using namespace std;

#define SQLHOST "10.33.53.27"

WebServer::WebServer(
            int port, int trigMode, int timeoutMS, bool OptLinger,
            int sqlPort, const char* sqlUser, const  char* sqlPwd,
            const char* dbName, int connPoolNum, int threadNum,
            bool openLog, int logLevel, int logQueSize):
            port_(port), openLinger_(OptLinger), timeoutMS_(timeoutMS),
            timer_(new HeapTimer()), threadpool_(new ThreadPool(threadNum)), epoller_(new Epoller())
    {
    //运行程序时所在的工作目录
    srcDir_ = getcwd(nullptr, 256);
    assert(srcDir_);
    strncat(srcDir_, "/resources/", 16);

    HttpConn::userCount = 0;//static
    HttpConn::srcDir = srcDir_;
    SqlConnPool::Instance()->Init(SQLHOST, sqlPort, sqlUser, sqlPwd, dbName, connPoolNum);

    InitEventMode_(trigMode);
    InitSocket_();

    if(openLog) {
      Log::Instance()->init(logLevel, "./log", ".log", logQueSize);
      LOG_INFO("========== Server init ==========");
      LOG_INFO("Port:%d, OpenLinger: %s", port_, OptLinger? "true":"false");
      LOG_INFO("Listen Mode: %s, OpenConn Mode: %s",
                      (listenEvent_ & EPOLLET ? "ET": "LT"),
                      (connEvent_ & EPOLLET ? "ET": "LT"));
      LOG_INFO("LogSys level: %d", logLevel);
      LOG_INFO("srcDir: %s", HttpConn::srcDir);
      LOG_INFO("SqlConnPool num: %d, ThreadPool num: %d", connPoolNum, threadNum);
    }
}

WebServer::~WebServer() {
    close(listenFd_);
    free(srcDir_);
    SqlConnPool::Instance()->ClosePool();
}

//listenEvent_:EPOLLRDHUP+EPOLLET
//connEvent_:EPOLLONESHOT+EPOLLRDHUP+EPOLLET
//EPOLLONESHOT:一次性,下次想要被通知需要再次注册事件
void WebServer::InitEventMode_(int trigMode) {
  listenEvent_ = EPOLLRDHUP;//对端(客户端)关闭
  connEvent_ = EPOLLONESHOT | EPOLLRDHUP;
  switch (trigMode)
  {
  case 0:
    break;
  case 1:
    connEvent_ |= EPOLLET;
    break;
  case 2:
    listenEvent_ |= EPOLLET;
    break;
  case 3:
    listenEvent_ |= EPOLLET;
    connEvent_ |= EPOLLET;
    break;
  default:
    listenEvent_ |= EPOLLET;
    connEvent_ |= EPOLLET;
    break;
  }
  HttpConn::isET = (connEvent_ & EPOLLET);
}
//程序开始处
void WebServer::Start() {
  int timeMS = -1;
  LOG_INFO("========== Server start ==========");
  while(1) {
    // if(timeoutMS_ > 0) {
    //   timeMS = timer_->GetNextTick();
    // }
    int eventCnt = epoller_->Wait(-1);//阻塞
    for(int i = 0; i < eventCnt; i++) {
      /* 处理事件 */
      int fd = epoller_->GetEventFd(i);
      uint32_t events = epoller_->GetEvents(i);
      if(fd == listenFd_) {
        DealListen_();
      }
      else if(events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
        assert(users_.count(fd) > 0);
        CloseConn_(&users_[fd]);
      }
      else if(events & EPOLLIN) {
        assert(users_.count(fd) > 0);
        DealRead_(&users_[fd]);
      }
      else if(events & EPOLLOUT) {
        assert(users_.count(fd) > 0);
        DealWrite_(&users_[fd]);
      } else {
        LOG_ERROR("Unexpected event");
      }
    }
  }
}

void WebServer::SendError_(int fd, const char*info) {
    assert(fd > 0);
    int ret = send(fd, info, strlen(info), 0);
    if(ret < 0) {
        LOG_WARN("send error to client[%d] error!", fd);
    }
    close(fd);
}

void WebServer::CloseConn_(HttpConn* client) {
    assert(client);
    LOG_INFO("Client[%d] quit!", client->GetFd());
    epoller_->DelFd(client->GetFd());
    client->Close();
}

//保存客户端连接信息
void WebServer::AddClient_(int fd, sockaddr_in addr) {
    assert(fd > 0);
    users_[fd].init(fd, addr);
    if(timeoutMS_ > 0) {
        timer_->add(fd, timeoutMS_, std::bind(&WebServer::CloseConn_, this, &users_[fd]));
    }
    epoller_->AddFd(fd, EPOLLIN | connEvent_);
    SetFdNonblock(fd);
    LOG_INFO("Client[%d] in!", users_[fd].GetFd());
}

void WebServer::DealListen_() {
  //客户端的地址信息
    struct sockaddr_in addr;
    socklen_t len = sizeof(addr);
    do {
        int fd = accept(listenFd_, (struct sockaddr *)&addr, &len);
        if(fd <= 0) { return;}
        else if(HttpConn::userCount >= MAX_FD) {
            SendError_(fd, "Server busy!");
            LOG_WARN("Clients is full!");
            return;
        }
        AddClient_(fd, addr);
    } while(listenEvent_ & EPOLLET);
}

//读取客户端发来的消息
void WebServer::DealRead_(HttpConn* client) {
    assert(client);
    ExtentTime_(client);//刷新时间
    threadpool_->AddTask(std::bind(&WebServer::OnRead_, this, client));
}
//向客户端写消息
void WebServer::DealWrite_(HttpConn* client) {
    assert(client);
    ExtentTime_(client);
    threadpool_->AddTask(std::bind(&WebServer::OnWrite_, this, client));
}
//刷新连接的过期时间
void WebServer::ExtentTime_(HttpConn* client) {
    assert(client);
    if(timeoutMS_ > 0) { timer_->adjust(client->GetFd(), timeoutMS_); }
}

//读取客户端发来的数据包
void WebServer::OnRead_(HttpConn* client) {
    assert(client);
    int ret = -1;
    int readErrno = 0;
    ret = client->read(&readErrno);
    if(ret <= 0 && readErrno != EAGAIN) {
        CloseConn_(client);
        return;
    }
    OnProcess(client);
}


void WebServer::OnProcess(HttpConn* client) {
    if(client->process()) {//解析客户端发来的数据包,并将发送给客户端的数据包准备好
        epoller_->ModFd(client->GetFd(), connEvent_ | EPOLLOUT);
    } else {
        epoller_->ModFd(client->GetFd(), connEvent_ | EPOLLIN);
    }
}

//向客户端发送数据包
void WebServer::OnWrite_(HttpConn* client) {
    assert(client);
    int ret = -1;
    int writeErrno = 0;
    ret = client->write(&writeErrno);
    if(client->ToWriteBytes() == 0) {
        /* 传输完成 */
        //本次连接不是keep-alive的,关闭连接
        //本次连接是keep-alive的,查看是否还有可读数据,没有就关闭连接
        //还有数据,再次监听EPOLLOUT,先不关闭连接,再次执行OnWrite_,再次检查是否keep-alive
        if(client->IsKeepAlive()) {
            OnProcess(client);
            // return;
        }
    }
    else if(ret < 0) {
        if(writeErrno == EAGAIN) {
            /* 继续传输 */
            epoller_->ModFd(client->GetFd(), connEvent_ | EPOLLOUT);
            return;
        }
    }
    CloseConn_(client);//这里关掉连接之后,才算完
}

//Create listenFd
void WebServer::InitSocket_() {
  if(port_ > 65535 || port_ < 1024) {
    puts("port is invalid.");
    exit(1);
  }

  //创建服务端socket
  listenFd_ = socket(AF_INET, SOCK_STREAM, 0);
  if(listenFd_ < 0) {
    perror("socket() failed");
    exit(1);
  }

  int ret;
  if(openLinger_) {
    /* 优雅关闭: 直到所剩数据发送完毕或超时 */
    struct linger optLinger = { 1,1 };
    ret = setsockopt(listenFd_, SOL_SOCKET, SO_LINGER, &optLinger, sizeof(optLinger));
    if(ret < 0) {
      close(listenFd_);
      perror("setsockopt() failed");
      exit(1);
    }
  }

  //端口复用
  //只有最后一个套接字会正常接收数据
  int optval = 1;
  ret = setsockopt(listenFd_, SOL_SOCKET, SO_REUSEADDR, (const void*)&optval, sizeof(int));
  if(ret == -1) {
    close(listenFd_);
    perror("setsockopt() failed");
    exit(1);
  }

  SetFdNonblock(listenFd_);

  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_port = htons(port_);
  ret = bind(listenFd_, (struct sockaddr *)&addr, sizeof(addr));
  if(ret < 0) {
    close(listenFd_);
    perror("bind() failed");
    exit(1);
  }

  ret = listen(listenFd_, 6);
  if(ret < 0) {
    close(listenFd_);
    perror("listen() failed");
    exit(1);
  }

  ret = epoller_->AddFd(listenFd_,  listenEvent_ | EPOLLIN);
  if(ret == 0) {
    puts("Add listen error!");
    close(listenFd_);
    exit(1);
  }
}

void WebServer::SetFdNonblock(int fd) {
  assert(fd > 0);
  int old_flag = fcntl(fd, F_GETFL);//2,表示O_RDWR
  fcntl(fd, F_SETFL, old_flag | O_NONBLOCK);//O_NONBLOCK是04000,也就是2048
  int new_flag = fcntl(fd, F_GETFL);//2050
}